package com.one.pass.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Date;
import java.util.concurrent.TimeUnit;

//RedisService类提供了一系列操作Redis的方法，包括基本的增删改查、锁机制以及Lua脚本执行。
@Service
@Slf4j
public class RedisService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Value("${spring.application.name}")
    private String applicationName;

    // 获取字符串类型的值
    public String getString(String key) {
        return redisTemplate.opsForValue().get(key, 0, Long.MAX_VALUE);
    }

    // 保存字符串类型的值，并设置超时时间
    public void saveString(String key, String value, int time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.MILLISECONDS);
    }

    // 获取带有应用名称前缀的字符串类型的值
    public String getStringWithApp(String key) {
        return (String) redisTemplate.opsForValue().get(applicationName + "::" + key);
    }

    // 保存带有应用名称前缀的字符串类型的值，并设置超时时间
    public void saveValueWithApp(String key, String data, long time) {
        redisTemplate.opsForValue().set(applicationName + "::" + key, data, time, TimeUnit.MILLISECONDS);
    }

    // 保存带有应用名称前缀的字符串类型的值
    public void saveValueWithApp(String key, String data) {
        redisTemplate.opsForValue().set(applicationName + "::" + key, data);
    }

    // 保存对象类型的值，并设置超时时间
    public <T> void saveObject(String key, T t, long time) {
        redisTemplate.opsForValue().set(key, t, time, TimeUnit.MILLISECONDS);
    }

    // 保存对象类型的值
    public <T> void saveObject(String key, T t) {
        redisTemplate.opsForValue().set(key, t);
    }

    // 获取对象类型的值
    public <T> T getObject(String key) {
        return (T) redisTemplate.opsForValue().get(key);
    }

    // 删除指定的键
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    // 尝试获取锁
    public boolean tryLock(String key, String value, long millisecond) {
        Boolean is = redisTemplate.opsForValue().setIfAbsent(key, value, millisecond, TimeUnit.MILLISECONDS);
        return is != null && is;
    }

    // 释放锁
    public void releaseLock(String key, String value) {
        if (value.equals(redisTemplate.opsForValue().get(key))) {
            redisTemplate.delete(key);
        }
    }

    // 尝试等待获取锁
    public boolean tryLockWait(String key, String value, long millisecond) {
        int limit = 40;
        int size = (int) (millisecond / limit);
        for (int i = 0; i < size; i++) {
            if (tryLock(key, value, millisecond)) {
                return true;
            }
            try {
                Thread.sleep(limit);
            } catch (InterruptedException e) {
                log.info("异常", e);
            }
        }
        return false;
    }

    // 执行增加计数的Lua脚本
    public Integer addCount(String key, Integer count, Integer time) {
        return redisTemplate.execute(getScript("lua/add_count.lua", Integer.class), Collections.singletonList(key), count, time);
    }

    // 执行增加计数并限制最大值的Lua脚本
    public Integer addCountLimit(String key, Integer count, Integer time, Integer limit) {
        return redisTemplate.execute(getScript("lua/add_count_limit.lua", Integer.class), Collections.singletonList(key), count, time, limit);
    }

    // 执行增加数字的Lua脚本
    public Integer addNum(String key, Integer count) {
        key = "calNum::" + key;
        Integer i = redisTemplate.execute(getScript("lua/add_num.lua", Integer.class), Collections.singletonList(key), count);
        return i;
    }

    // 获取Lua脚本
    private <T> DefaultRedisScript<T> getScript(String file, Class<T> cls) {
        DefaultRedisScript<T> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(file)));
        redisScript.setResultType(cls);
        return redisScript;
    }

    // 删除指定前缀的键
    public void remove(String key) {
        delete("calNum::" + key);
    }
}